class AppExample {

    // Общий вид лямбды
    // (параметры) -> выражение
    // или
    // (параметры) -> {операторы;}
    // Стрелка — это лямбда-оператор
    // Чаще всего используются три функциональных интерфейса:
    // Predicate - принимает параметр и возвращает логическое значение
    // Consumer - который принимает параметр и не возвращает никакого значения
    // Function - принимает параметр и возвращает какое-то значение значение

    public static List<String> getPetNames(List<Map<String, String>> animals, String animal) {
        return animals.stream()
            // Сначала отфильтруем всех питомцев, например только собак
            // Метод filter принимает в качестве параметра Predicate
            // Лямбда принимает вид питомца и возвращает true, если оно совпадает с требуемым
            .filter(pet -> pet.get("animal").equals(animal))
            // Затем сделаем отображение, оставим только имя питомца
            // Метод filter принимает на вход Function
            // Она принимает текущий элемент стрима и возвращает новый измененный
            .map(pet -> pet.get("name"))
            // Сортируем имена
            // Метод sorted принимает на вход Function
            // Лямбда принимает два соседних элемента стрима и возвращает 1, 0 или -1
            // в зависимости от того, больше ли первый элемент чем второй, меньше или равен
            .sorted((name1, name2) -> name1.compareTo(name2))
            .collect(Collectors.toList());
    }
}
